package org.luxor.commons.core.config;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import org.luxor.commons.core.config.properties.ExceptionProperties;
import org.luxor.commons.core.config.properties.InfoProperties;
import org.luxor.commons.core.utils.DateUtils;
import org.luxor.commons.core.utils.Jackson2Utils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.web.ResourceProperties;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration;
import org.springframework.boot.autoconfigure.web.servlet.WebMvcProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.*;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.TimeZone;

/**
 * SpringMVC 配置类
 *
 * @author Mr.yan @date 2019/9/19
 */
@Configuration
@EnableWebMvc
@EnableConfigurationProperties({ResourceProperties.class, WebMvcProperties.class, InfoProperties.class, ExceptionProperties.class})
@ConditionalOnMissingClass("org.luxor.commons.mvc.config.WebMvcConfig")
public class WebMvcConfig extends WebMvcAutoConfiguration implements WebMvcConfigurer {

    private final ResourceProperties resourceProperties;

    private final WebMvcProperties webMvcProperties;

    public WebMvcConfig(ResourceProperties resourceProperties, WebMvcProperties webMvcProperties) {
        this.resourceProperties = resourceProperties;
        this.webMvcProperties = webMvcProperties;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler(webMvcProperties.getStaticPathPattern()).addResourceLocations(resourceProperties.getStaticLocations());
    }

    @Override
    public void addFormatters(FormatterRegistry registry) {
        ZoneId zoneId = TimeZone.getTimeZone("GMT+8").toZoneId();
        DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
        registrar.setDateFormatter(DateTimeFormatter.ofPattern(DateUtils.DATE_PATTERN).withZone(zoneId));
        registrar.setTimeFormatter(DateTimeFormatter.ofPattern(DateUtils.TIME_PATTERN).withZone(zoneId));
        registrar.setDateTimeFormatter(DateTimeFormatter.ofPattern(DateUtils.DATE_TIME_PATTERN).withZone(zoneId));
        registrar.registerFormatters(registry);
    }

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.removeIf(converter -> converter instanceof StringHttpMessageConverter || converter instanceof MappingJackson2HttpMessageConverter);
        converters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));

        SimpleModule simpleModule = new SimpleModule();
        // 解决long类型精度丢失问题
        simpleModule.addSerializer(BigInteger.class, com.fasterxml.jackson.databind.ser.std.ToStringSerializer.instance);
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        // 解决BigDecimal类型精度丢失问题
        simpleModule.addSerializer(BigDecimal.class, Jackson2Utils.BigDecimalSerializer.instance);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(simpleModule);
        // 设置为空的字段不返回
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        // 反序列化时候，忽略多余的字段
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 指定json转换时间类型的时区
        objectMapper.setTimeZone(TimeZone.getTimeZone("GMT+8"));
        // 指定返回的时间格式
        objectMapper.setDateFormat(new SimpleDateFormat(DateUtils.DATE_TIME_PATTERN));

        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        jackson2HttpMessageConverter.setObjectMapper(objectMapper);
        converters.add(jackson2HttpMessageConverter);
    }

    @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer.setUseSuffixPatternMatch(false)
                .setUseTrailingSlashMatch(true);
    }

    @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.favorPathExtension(false);
    }

}